/**
 * @export
 * @class River
 * @classdesc 河流组件
 * @property {Object} 地图容器 - SchoolLab实例
 * @property {String} name - 名称
 * @property {String} color - 颜色
 * @property {Number} opacity - 透明度
 * @property {Number} radius - 半径
 * @property {Number} renderOrder - 层叠顺序
 * @property {String} noise - 河流噪音图
 * @property {Number} repeat - 贴图重复次数
 * @property {Array} path - 轮廓路径数据
 * @example
 * var river = new River(viewer, {
 *   path: [],
 *   color: '#ff0000',
 *   opacity: 0.5,
 *   renderOrder: 20,
 *   name: 'river',
 *   repeat: 10
 * })
 */
import { extend, isArray, lngLatToWorld, transformCoordinate } from './util'
import Feature from './Feature'
import Guid from './Guid'
export default class River {
  constructor(viewer, options) {
    const opts = extend({
      name: "river",
      color: "#00BFFF",
      opacity: 1,
      repeat: 1,
      noise: require('../../assets/images/river_noise.png'),
      renderOrder: 1
    }, options)


    /** @member {SchoolLab} - 地图容器 */
    this.viewer = viewer
    
    /** @member {String} - 名称 */
    this.name = opts.name
    
    /** @member {Color} - 颜色 */
    this.color = opts.color
    
    /** @member {Number} - 透明度 */
    this.opacity = opts.opacity
    
    /** @member {String} - 噪音贴图 */
    this.noise = opts.noise
    
    /** @member {Number} - 重复次数 */
    this.repeat = opts.repeat
    
    /** @member {Array} - 轮廓路径集 */
    this.path = opts.path
    
    /** @member {Number} - 层叠顺序 */
    this.renderOrder = opts.renderOrder
    
    /** @member {THREE.Mesh} - mesh对象 */
    this.entity = null
    
    /** @member {String} - 唯一id标识 */
    this.id = Guid.create()

    Feature.apply(this)

    this._init()
  }

  // 初始化组件
  _init() {
    this.viewer.animationManager.regist(`water_${this.id}`, this._update.bind(this))
    let geometry = this._initGeometry()
    let material = this._initMaterial()
    
    const mesh = new THREE.Mesh(geometry, material)
    mesh.name = this.name
    mesh.renderOrder = this.renderOrder
    
    this.entity = mesh
    this.viewer.world.add(mesh)
  }

  // 初始化几何体
  _initGeometry() {
    const formatPath = this._formatPolygonPath(this.path)
    const _path = this._getGeographicPolygonFormat(this.viewer.gisCenter, formatPath)
    const geometry = new THREE.Geometry()

    for (let i = 0; i < _path.length; i++) {
      if (isArray(_path[i][0])) {
        let shape = null
        for (let j = 0; j < _path[i].length; j++) {
          if (j === 0) {
            shape = new THREE.Shape(_path[i][j])
          } else {
            shape.holes.push(new THREE.Path(_path[i][j]))
          }
        }
        const unitGeo = new THREE.ShapeGeometry(shape)
        let unitMesh = new THREE.Mesh(unitGeo, new THREE.MeshBasicMaterial())
        unitMesh.updateMatrix()
        geometry.merge(unitMesh.geometry, unitMesh.matrix)
        unitMesh.geometry.dispose()
        unitMesh.material.dispose()
        unitMesh = null
      } else {
        const unitGeo = new THREE.ShapeGeometry(new THREE.Shape(_path[i]))
        let unitMesh = new THREE.Mesh(unitGeo, new THREE.MeshBasicMaterial())
        unitMesh.updateMatrix()
        geometry.merge(unitMesh.geometry, unitMesh.matrix)
        unitMesh.geometry.dispose()
        unitMesh.material.dispose()
        unitMesh = null
      }
    }
    // 格式化uv关系
    geometry.computeBoundingBox()
    const max = geometry.boundingBox.max
    const min = geometry.boundingBox.min
    const offset = new THREE.Vector2(0 - min.x, 0 - min.y)
    const range = new THREE.Vector2(max.x - min.x, max.y - min.y)
    const faces = geometry.faces
    geometry.faceVertexUvs[0] = []
    for (let i = 0; i < faces.length; i++) {
      const v1 = geometry.vertices[faces[i].a]
      const v2 = geometry.vertices[faces[i].b]
      const v3 = geometry.vertices[faces[i].c]
      geometry.faceVertexUvs[0].push([
        new THREE.Vector2((v1.x + offset.x) / range.x, (v1.y + offset.y) / range.y),
        new THREE.Vector2((v2.x + offset.x) / range.x, (v2.y + offset.y) / range.y),
        new THREE.Vector2((v3.x + offset.x) / range.x, (v3.y + offset.y) / range.y)
      ])
    }
    geometry.uvsNeedUpdate = true

    return new THREE.BufferGeometry().fromGeometry(geometry)
  }

  // 初始化材质
  _initMaterial() {
    let loader = new THREE.TextureLoader()
    const noise = loader.load(this.noise)
    noise.wrapS = noise.wrapT = THREE.RepeatWrapping

    const uniforms = {
      time: { type: 'f', value: 0 },
      color: { type: 'c', value: new THREE.Color(this.color) },
      opacity: { type: 'f', value: this.opacity },
      repeat: { type: 'f', value: this.repeat },
      noise_map: { type: 't', value: noise }
    }

    let vs = this._initVS()
    let fs = this._initFS()
    const material = new THREE.ShaderMaterial({
      uniforms: uniforms,
      vertexShader: vs,
      fragmentShader: fs,
      blending: THREE.NormalBlending,
      transparent: true,
      depthWrite: false,
      depthTest: true,
      side: THREE.DoubleSide
    })

    return material
  }

  // 初始化vertexShader
  _initVS() {
    let vs = `
      precision highp float;
      precision highp int;
      varying vec2 vUv;
      void main(){
        vUv = uv;
        gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
      }
    `
    return vs
  }

  // 初始化fragmentShader
  _initFS() {
    let fs = `
      precision highp float;
      precision highp int;
      uniform vec3 color;
      uniform float opacity;
      varying vec2 vUv;
      uniform float time;
      uniform float repeat;
      uniform sampler2D noise_map;
      
      float water_deep = 0.8;
      
      float coast2water_fadedepth = 0.3;
      
      float large_wavesize  = 0.8;  
      float large_waveheight = 0.40; 
      float small_wavesize = 0.5;   
      
      #define package(x,y) (x * y)
      
      #define water_level package(water_deep,2.)
      #define water_fadedepth package(coast2water_fadedepth,0.5)
      
      #define waveIteration 7
      
      float water_softlight_fact  = 15.;  // range [1..200] (should be << smaller than glossy-fact)
      float water_glossylight_fact= 180.; // range [1..200]
      
      vec3 light;
      
      float small_waveheight = .6;  // change to adjust the small random waves
      
      float particle_amount = 70.;
      vec3 water_specularcolor = vec3(1., 1., 1.);    // specular Color (RGB) of the water-highlights
      
      float noise(vec2 p) {
        return texture2D(noise_map, p*vec2(1./256.)).x;
      }
      
      float height_map( vec2 p ) {
        float f = 0.15;
        return clamp(f, 0., 10.);
      }
      
      const mat2 m = mat2( 0.72, -1.60,  1.60,  0.72 );
      
      float water_map( vec2 p, float height ) {
        vec2 p2 = p*large_wavesize;
        vec2 shift1 = 0.001*vec2( time*160.0*2.0, time*120.0*2.0 );
        vec2 shift2 = 0.001*vec2( time*190.0*2.0, -time*130.0*2.0 );
      
        // coarse crossing 'ocean' waves...
        float f = 0.6*noise( p );
        
          f += 0.2500*noise(  p *m );
          f += 0.1666*noise( p*m*m );
        float wave = sin(p2.x*0.622+p2.y*0.622+shift2.x*4.269)*large_waveheight*f*height*height ;
      
        p *= small_wavesize;
        f = 0.;
        float amp = 1.0, s = .5;
        //对波浪进行分面  
        for (int i=0; i<waveIteration; i++)
        { 
            p = m*p*.947; f -= amp*abs(sin((noise( p+shift1*s )-.5)*2.)); amp = amp*.59; s*=-1.329;
        }
      
        return wave+f*small_waveheight;
      }
      
      float test_shadow( vec2 xy, float height) {
        vec3 r0 = vec3(xy, height);
        vec3 rd = normalize( light - r0 );
        
        float hit = 1.0;
        float t   = 0.001;
        for (int j=1; j<5; j++)
        {
            vec3 p = r0 + t*rd;
            float h = height_map( p.xy );
            float height_diff = p.z - h;
            if (height_diff<0.0)
            {
                return 0.0;
            }
            t += 0.01+height_diff*.02;
            hit = min(hit, 2.*height_diff/t); // soft shaddow   
        }
        return hit;
      }
      
      void main() {
        vec3 watercolor = color; // 'transparent' low-water color (RGB)
        vec3 watercolor2 = color * 0.25; // deep-water color (RGB, should be darker than the low-water color)

        vec3 col;
        light = vec3(0.5, 0.4,3.5); 
          
        vec2 _uv = vUv * repeat;
        vec2 uv = vec2(mod(_uv.x, 1.), mod(_uv.y, 1.));
        float deepwater_fadedepth   = .5 + water_fadedepth;
        float waveheight = water_level;
      
        
        float height = height_map( uv );
      
        float level = 10.;
        vec2 dif = vec2(.0, .01);
        vec2 pos = uv*15. + vec2(time*.01);
        float h1 = water_map(pos-dif,waveheight);
        float h2 = water_map(pos+dif,waveheight);
        float h3 = water_map(pos-dif.yx,waveheight);
        float h4 = water_map(pos+dif.yx,waveheight);
        vec3 normwater = normalize(vec3(h3-h4, h1-h2, .125)); // norm-vector of the 'bumpy' water-plane
      
      
        float coastfade = clamp((level-height)/water_fadedepth, 0., 1.);
        float coastfade2= clamp((level-height)/deepwater_fadedepth, 0., 1.);
        float intensity = col.r*.2126+col.g*.7152+col.b*.0722;
        watercolor = mix(watercolor*intensity, watercolor2, smoothstep(0., 1., coastfade2));
      
        vec3 r0 = vec3(uv, water_level);
        vec3 rd = normalize( light - r0 ); // ray-direction to the light from water-position
        float grad     = dot(normwater, rd); // dot-product of norm-vector and light-direction
        float specular = pow(grad, water_softlight_fact);  // used for soft highlights                          
        float specular2= pow(grad, water_glossylight_fact); // used for glossy highlights
        float gradpos  = dot(vec3(0., 0., 1.), rd);
        float specular1= smoothstep(0., 1., pow(gradpos, 5.));  // used for diffusity (some darker corona around light's specular reflections...)                          
        float watershade  = test_shadow( uv, level );
      
        watercolor *= 2.2+watershade;
        watercolor += (.2+.8*watershade) * (.5+specular) * .05;
        watercolor /= (1.+specular1*.15);
        watercolor += watershade*specular2*water_specularcolor * 0.41;
      
        watercolor += watershade*coastfade*(1.-coastfade2);
      
        col = mix(col, watercolor, coastfade);
      
        gl_FragColor = vec4(col * color , opacity);
      }
    `
    return fs
  }

  // 格式化轮廓路径点集
  _formatPolygonPath(geojsonArr) {
    const formatPath = []
    for (let i = 0; i < geojsonArr.length; i++) {
      const type = geojsonArr[i].geometry && geojsonArr[i].geometry.type
      if (type === 'MultiPolygon') {
        const coordinates = geojsonArr[i].geometry.coordinates
        let j = 0
        while (j < coordinates.length) {
          const coordinate = coordinates[j]
  
          if (coordinate.length === 1) {
            const _coordinate = coordinate[0]
            const path = []
            for (let k = 0, len = _coordinate.length; k < len; k++) {
              path.push(new THREE.Vector3(_coordinate[k][0], _coordinate[k][1], 0))
            }
            formatPath.push(path)
          } else {
            const path = []
            for (let k = 0; k < coordinate.length; k++) {
              const _coordinate = coordinate[k]
              const _path = []
              for (let l = 0, len = _coordinate.length; l < len; l++) {
                _path.push(new THREE.Vector3(_coordinate[l][0], _coordinate[l][1], 0))
              }
              path.push(_path)
            }
            formatPath.push(path)
          }
          j++
        }
      } else if (type === 'Polygon') {
        const coordinates = geojsonArr[i].geometry.coordinates
        if (coordinates.length === 1) {
          const coordinate = coordinates[0]
          const path = []
          for (let j = 0, len = coordinate.length; j < len; j++) {
            path.push(new THREE.Vector3(coordinate[j][0], coordinate[j][1], 0))
          }
          formatPath.push(path)
        } else {
          const path = []
          for (let j = 0; j < coordinates.length; j++) {
            const coordinate = coordinates[j]
            const _path = []
            for (let k = 0, len = coordinate.length; k < len; k++) {
              _path.push(new THREE.Vector3(coordinate[k][0], coordinate[k][1], 0))
            }
            path.push(_path)
          }
          formatPath.push(path)
        }
      } else if (!type) {
        const coordinates = geojsonArr[i]
        const path = []
        for (let j = 0; j < coordinates.length; j++) {
          const coordinate = coordinates[j]
          const _path = []
          for (let k = 0, len = coordinate.length; k < len; k++) {
            _path.push(coordinate[k])
          }
          path.push(_path)
        }
        formatPath.push(path)
      }
    }
    return formatPath
  }

  // gis坐标转相对世界坐标
  _getGeographicPolygonFormat(gisCenterPos, path) {
    const _path = []
  
    for (let i = 0; i < path.length; i++) {
      const unitPath = path[i]
      if (isArray(unitPath[0])) {
        const formatPath = []
        for (let j = 0; j < unitPath.length; j++) {
          let _formatPath = []
          _formatPath = unitPath[j].map((vec) => {
            const pos = lngLatToWorld([vec.x, vec.y])
            const format = transformCoordinate(gisCenterPos, pos)
            return new THREE.Vector3(format.x, format.y, vec.z)
          })
          formatPath.push(_formatPath)
        }
        _path.push(formatPath)
      } else {
        let formatPath = []
        formatPath = unitPath.map((vec) => {
          const pos = lngLatToWorld([vec.x, vec.y])
          const format = transformCoordinate(gisCenterPos, pos)
          return new THREE.Vector3(format.x, format.y, vec.z)
        })
        _path.push(formatPath)
      }
    }

    return _path
  }

  // 动画系数更新
  _update() {
    this.entity.material.uniforms.time.value += 0.1
  }

  // 组件销毁
  destroy() {
    this._disposeHierarchy(this.entity, this._dispose)
    this.viewer.world.remove(this.entity)
    this.entity = null
    this.viewer.animationManager.unload(`water_${this.id}`)
  }
}
